/*
 * ComHandler class 
 * this class responsible on communication with other device
 * the CommHandler send message to other device every message contain:
 * 1. counterSendMessage (every send message is bigger)
 * 2. type of message (long or string)
 * 3. state of application
 * 4. data
 * MyMessage class 
 */
package com.warrior.bluetooth;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.warrior.bluetooth.WaitToCallBack.IFinishTime;
import com.warrior.games.Game.GAME_STATES;
import com.warrior.main.MyApp;
import com.warrior.main.MyApp.APP_STATES;
import com.warrior.main.MyLog;

import android.bluetooth.BluetoothSocket;
import android.os.AsyncTask;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.style.LineHeightSpan.WithDensity;
import android.util.Log;
import android.widget.Toast;

public class CommHandler implements IFinishTime
{

	private BluetoothSocket socket;
	private InputStream inStream;
	private OutputStream outStream;
	private IDateReceive iDateReceive;
	private boolean isConnected = false;
	private MyApp app;
	static private int counterSendMessages = 0;
	private WaitToCallBack waitToCallBack;
	Thread waitTimeThread;
	
	public final static long DISCONNECTED = 1000000;
	
	public CommHandler(MyApp app,BluetoothSocket socket) throws IOException
	{
		this.socket = socket;
		this.app = app;
		inStream = socket.getInputStream();
		outStream = socket.getOutputStream();
		isConnected = true;
		waitToCallBack = new WaitToCallBack();
		waitToCallBack.setFinishTimeListener(this);
	}
	public boolean isConnected() {
		return isConnected;
	}
	public void setDateReceiveListener(IDateReceive iDateReceive) {
		this.iDateReceive = iDateReceive;
	}
	private void writeMessage(String message) 
	{
		try {
			Log.d(MyLog.COMM,"before send data: " + message);
			byte[] byteString = (message + " ").getBytes();
			outStream.write(byteString);
			outStream.flush();
			counterSendMessages++;
			Log.d(MyLog.COMM,"after send data: " + message);

		} catch (IOException e) {
			Log.d(MyLog.COMM_EXEPTION,"IO Exception when writing to remote device message: " + e.getMessage());
		} 

	}
	public void writeToRmoteDevice(String data) {
		String message = MyMessage.buildSendMessage(
				counterSendMessages,TYPE_DATA.word.ordinal(),app.getState().ordinal(),data,false);
		writeMessage(message);
	}
	public void writeToRmoteDevice(Long data) {
		String message = MyMessage.buildSendMessage(
				counterSendMessages,TYPE_DATA.number.ordinal(),app.getState().ordinal(),data,false);
		writeMessage(message);
	}
	public void writeToRmoteDeviceWithCallBack(String data) {
		final String message = MyMessage.buildSendMessage(
				counterSendMessages,TYPE_DATA.word.ordinal(),app.getState().ordinal(),data,true);
		startWaitTimeThread(message);
		writeMessage(message);
	}
	public void writeToRmoteDeviceWithCallBack(Long data) {
		final String message = MyMessage.buildSendMessage(
				counterSendMessages,TYPE_DATA.number.ordinal(),app.getState().ordinal(),data,true);
		startWaitTimeThread(message);
		writeMessage(message);
	}
	private void startWaitTimeThread(String message){
		//waitTimeThread.interrupt();
		waitTimeThread = null;
		waitTimeThread = new Thread(waitToCallBack);
		waitToCallBack.setMessage(message);
		waitTimeThread.start();
	}
	public void closeConnection()
	{
		try {
			Log.d(MyLog.COMM,"into connection close");
			counterSendMessages = 0;
			isConnected = false;
			if(inStream != null)
			{
				inStream.close();
				inStream = null;

			}
			if(outStream != null)
			{
				outStream.close();
				outStream = null;
			}
			if(socket != null)
			{
				socket.close();
				socket = null;
			}
		} catch (IOException e) {
			Log.d(MyLog.COMM_EXEPTION,"IOException while trying to close connection: message: " + e.getMessage());
		}

	}
	public void startReceiveDataListener(){
		new ReceiveData().execute();
	}
	class ReceiveData extends AsyncTask<Void, MyMessage, Void>{

		protected Void doInBackground(Void... params) {
			 int bufferSize = 1024;
			 Long receiveTime;
			 byte[] buffer = new byte[bufferSize];
			 try {
				 int bytesRead=-1;
				 String message;
				 while (isConnected) {
					 message = "";
					 bytesRead = inStream.read(buffer);
					 if (bytesRead != -1) {
						 while ((bytesRead==bufferSize)&&(buffer[bufferSize-1] != 0)) {
							 message = message + new String(buffer, 0, bytesRead);
							 bytesRead = inStream.read(buffer);
						 }
						 message = message + new String(buffer, 0, bytesRead - 1);
						 // message has arrived 
						 // get system time
						 receiveTime = Sync.getSystemTime();
						 // build MyMessage 
						 MyMessage myMessage= MyMessage.buildReceiveMessage(message);
						 if(myMessage!= null){
							 Log.d(MyLog.COMM,"message received:" + myMessage.toString());
							 myMessage.setReceiveTime(receiveTime);
							 // send message to UI thread
							 publishProgress(myMessage);
						 }
					 }
				 }
			 } catch (IOException e) {
				 Log.d(MyLog.COMM_EXEPTION, "this exception throw becuause instream.read(buffer)" +
				 		"if this exception throw after bluetooth disconnected is good exception message:" + e.getMessage());
			 }
			return null;
		}
		protected void onProgressUpdate(MyMessage... messages) {
			// send myMessage to MyApp class
			if(messages[0].getType() == TYPE_DATA.number){
				if(messages[0].getLData() == MyApp.DATA_ARRIVED){
					waitTimeThread = null;
					waitToCallBack.setMessageArrived();
					Log.d(MyLog.COMM,"message arrived");
				}
			}
			iDateReceive.dataReceive(messages[0]);

		}
	}
	@Override
	public void finishTime(String msg) {
		writeMessage(msg);
		Log.d(MyLog.COMM,"message not arrived we send message try agin");
		//Log.d(MyLog.COMM,msg.toString());
	}
	public interface IDateReceive{
		void dataReceive(MyMessage message);
	}

}